package chezsmithybot;

import java.util.List;
import java.util.ArrayList;
import java.util.logging.Logger;
import com.google.wave.api.*;

import javax.jdo.PersistenceManager;

import com.google.wave.api.Blip;
import com.google.wave.api.Event;
import com.google.wave.api.TextView;
import com.google.wave.api.Wavelet;
import com.google.wave.api.impl.WaveletData;

public class CommandEngine {
	
	private static final Logger log = Logger
    .getLogger(ChezsmithybotServlet.class.getName());
	
	@SuppressWarnings("unchecked")
	public void commandParser(Event e,Wavelet wavelet,String blipText) {
    	
    	String answerText = "";
    	String[] commands = null;
    	
    	// split the commands using one or more spaces.
    	commands = blipText.split("\\s+");
    	
    	// blipChildWriter (e,"Command Received: " + commands[0].substring(1));
    	
    	if (blipText.contains("/saveblip")) {
    		commandSaveBlip(e,wavelet);
    		return;
    	}
    	
		switch (Command.toCommand(commands[0].substring(1))) {
		
		case ADDWAVE:
			commandAddWave(wavelet);
			break;
		case PRINTBLIPS:
			commandPrintBlips(e,wavelet);
			break;
		case DEFRIEND:
			// remove from all waves where a tile is stored
			commandDefriend(e,wavelet);
			break;
		case NAME:
			commandName(e);
			break;
		case CREATOR:
			commandCreator(e);
			break;
		case CLEANHOUSE:
			commandCleanHouse(e, wavelet);
			break;
		case HELP:
			commandHelp(wavelet);
			break;
		case PUSH:
			commandPush(e, wavelet, commands);
			break;
		case POP:
			commandPop(e, wavelet);
			break;
		case ADDSTACK:
			commandAddStack(e, wavelet, blipText);
			break;
		case PRINTSTACK:
			commandPrintStack(e, wavelet);
			break;
		case SAVETITLE:
			commandSaveTitle(e, wavelet);
			break;
		case PRINTTITLE:
			commandPrintTitle(e, wavelet);
			break;
		case DELETE:
			commandDelete(wavelet);
			break;
		default:
			commandNoValue(e);
			break;
		}
    }

	private void commandNoValue(Event e) {
		blipChildWriter(e,"Command unrecognized. Try /help");
	}
	
	private void commandDelete(Wavelet wavelet) {
		PersistenceManager deletepm = PMF.get().getPersistenceManager();
		String deletequery = "select from " + WaveStack.class.getName() + " where waveID==\"" + wavelet.getWaveId() + "\"";
		try {
			List<WaveStack> deletewavestacks = (List<WaveStack>) deletepm.newQuery(deletequery).execute();
			if (!deletewavestacks.isEmpty()) {
				deletewavestacks.remove(0);
			}
		} catch (Exception ex) {
			if (!deletepm.isClosed()) {
				deletepm.close();
			}
		}
	}
	
	private void commandDefriend(Event e, Wavelet wavelet) {
		blipChildWriter(e,"Leaving the wave, defriending all of you.");
		wavelet.removeParticipant("chezsmithy-bot@appspot.com");
	}

	private void commandPrintTitle(Event e, Wavelet wavelet) {
		String answerText;
		PersistenceManager printtitlepm = PMF.get().getPersistenceManager();
		String printtitlequery = "select from " + WaveContent.class.getName() + " where waveID==\"" + wavelet.getWaveId() + "\"";
		try {
			List<WaveContent> printwavetitles = (List<WaveContent>) printtitlepm.newQuery(printtitlequery).execute();
			if (printwavetitles.isEmpty()) {
				answerText = ("No tile saved?  Try /savetitle first.");
				blipChildWriter(e,answerText);    						
			} else {
				answerText = ("You saved a wave title of: " + printwavetitles.get(0).getWaveTitle() + " and the current title is: " + wavelet.getTitle());
				blipChildWriter(e,answerText);
			}
		} catch (Exception ex) {
			answerText = ("No tile saved?  Try /savetitle first.");
			blipChildWriter(e,answerText); 
			if (!printtitlepm.isClosed()) {
				printtitlepm.close();
			}
		}
	}
	
	private void commandPrintBlips(Event e, Wavelet wavelet) {
		String answerText = null;
		PersistenceManager pm = PMF.get().getPersistenceManager();
		String query = "select from " + WaveContent.class.getName() + " where waveID==\"" + wavelet.getWaveId() + "\"";
		try {
			List<WaveContent> wavecontents = (List<WaveContent>) pm.newQuery(query).execute();
			answerText = "Saved Blips: ";
			if (wavecontents.isEmpty()) {
				answerText = ("No blips saved?  Try /saveblip first.");
				blipChildWriter(e,answerText);    						
			} else {
				for (String blip : wavecontents.get(0).getBlips()) {
					answerText += blip + " ";
				}
				blipChildWriter(e,answerText);
			}
		} catch (Exception ex) {
			answerText = ("No blips saved?  Try /saveblip first.");
			blipChildWriter(e,answerText); 
			if (!pm.isClosed()) {
				pm.close();
			}
		}
	}

	private void commandSaveTitle(Event e, Wavelet wavelet) {
		String answerText;
		String waveTitle = wavelet.getTitle();
			
		PersistenceManager wavetitlepm = PMF.get().getPersistenceManager();
		
		try {
			String query = "select from " + WaveContent.class.getName() + " where waveID==\"" + wavelet.getWaveId() + "\"";
			List<WaveContent> wavecontents = (List<WaveContent>) wavetitlepm.newQuery(query).execute();
			
			if (!(wavecontents.isEmpty())) {
				log.info("SAVETITLE: Found existing value");
				wavecontents.get(0).setWaveTitle(waveTitle);
			} else {
				log.info("SAVETITLE: Adding new value");
				WaveContent wavecontent = new WaveContent();
				wavecontent.setWaveID(wavelet.getWaveId());
				wavecontent.setWaveTitle(waveTitle);
				wavetitlepm.makePersistent(wavecontent);
			}
		} finally {
			if (!wavetitlepm.isClosed()) {
				wavetitlepm.close();
			}
		}
		answerText = ("I saved the wavetitle: ") + waveTitle;
		blipChildWriter(e,answerText);
	}
	
	private void commandSaveBlip(Event e, Wavelet wavelet) {
		String answerText;
		String blipText = e.getBlip().getDocument().getText();
		String withoutCommand = blipText.substring(0, blipText.indexOf("/saveblip"));
			
		PersistenceManager pm = PMF.get().getPersistenceManager();
		
		try {
			String query = "select from " + WaveContent.class.getName() + " where waveID==\"" + wavelet.getWaveId() + "\"";
			List<WaveContent> wavecontents = (List<WaveContent>) pm.newQuery(query).execute();
			
			if (!(wavecontents.isEmpty())) {
				wavecontents.get(0).setBlip(withoutCommand);
			} else {
				WaveContent wavecontent = new WaveContent();
				wavecontent.setWaveID(wavelet.getWaveId());
				wavecontent.setWaveTitle(wavelet.getTitle());
				wavecontent.setBlip(withoutCommand);
				pm.makePersistent(wavecontent);
			}
		} finally {
			if (!pm.isClosed()) {
				pm.close();
			}
		}
		answerText = ("I saved the blip: ") + withoutCommand;
		blipChildWriter(e,answerText);
	}

	private void commandPrintStack(Event e, Wavelet wavelet) {
		String answerText;
		PersistenceManager printstackpm = PMF.get().getPersistenceManager();
		String printstackquery = "select from " + WaveStack.class.getName() + " where waveID==\"" + wavelet.getWaveId() + "\"";
		try {
			List<WaveStack> printwavestacks = (List<WaveStack>) printstackpm.newQuery(printstackquery).execute();
			if (printwavestacks.isEmpty()) {
				answerText = ("No results on the stack to add?  Try /push first.");
				blipChildWriter(e,answerText);    						
			} else {
				answerText = printwavestacks.get(0).printstack();
				blipChildWriter(e,answerText);
			}
		} catch (Exception ex) {
			answerText = ("No results on the stack to print?  Try /push first.");
			blipChildWriter(e,answerText); 
			if (!printstackpm.isClosed()) {
				printstackpm.close();
			}
		}
	}

	private void commandAddStack(Event e, Wavelet wavelet, String blipText) {
		String answerText;
		PersistenceManager addstackpm = PMF.get().getPersistenceManager();
		String addstackquery = "select from " + WaveStack.class.getName();
		try {
			List<WaveStack> addwavestacks = (List<WaveStack>) addstackpm.newQuery(addstackquery).execute();
			if (addwavestacks.isEmpty()) {
				answerText = ("No results on the stack to add?  Try /push first.");
				blipChildWriter(e,answerText);    						
			} else {
				Integer result = 0;
				for (WaveStack w : addwavestacks) {
					if (blipText.contains("/allwaves")) {
						// if the allwaves command is included
						// ignore the current waveID and sum all the stacks which are populated
						result += w.sumstack();
					} else {
						// otherwise just add up the stack for the current wave.
						if (w.getwaveID().equalsIgnoreCase(wavelet.getWaveId())) {
							result = w.sumstack();
						}
					}
				}
				answerText = ("I have added up the numbers on the Stack and they equal " + result);
				blipChildWriter(e,answerText);
			}
		} catch (Exception ex) {
			answerText = ("No results on the stack to add?  Try /push first.");
			blipChildWriter(e,answerText); 
			if (!addstackpm.isClosed()) {
				addstackpm.close();
			}
		}
	}

	private void commandPop(Event e, Wavelet wavelet) {
		String answerText;
		PersistenceManager poppm = PMF.get().getPersistenceManager();
		
		try {
			String popquery = "select from " + WaveStack.class.getName() + " where waveID==\"" + wavelet.getWaveId() + "\"";
			List<WaveStack> popwavestacks = (List<WaveStack>) poppm.newQuery(popquery).execute();
		
			try {
					String popVal = popwavestacks.get(0).popstackVal();
					if (popVal.isEmpty()) {
						answerText = ("Nothing on the stack to pop!");
						blipChildWriter(e,answerText);
					} else {
						answerText = ("Popped the value ") + popVal;
						blipChildWriter(e,answerText);
					}
			} finally {
				poppm.close();
			}
			
		} catch (Exception e1) {
			answerText = ("Nothing on the stack to pop!");
			blipChildWriter(e,answerText);
			if (!poppm.isClosed()) {
				poppm.close();
			}
		}
	}

	private void commandPush(Event e, Wavelet wavelet, String[] commands) {
		String answerText;
		Float stackVal;
		
		if (commands.length < 2) {
			blipChildWriter(e,"The /push command expects an argument.");
			return;
		}
		
		try {
			stackVal = Float.valueOf(commands[1]);
		} catch (NumberFormatException ex) {
			// catch where push doesn't receive a number
			blipChildWriter(e,"You must supply a number to the /push command.");
			return;
		}
		
		PersistenceManager pm = PMF.get().getPersistenceManager();
		
		try {
			String query = "select from " + WaveStack.class.getName() + " where waveID==\"" + wavelet.getWaveId() + "\"";
			List<WaveStack> wavestacks = (List<WaveStack>) pm.newQuery(query).execute();
			
			if (!(wavestacks.isEmpty())) {
				log.info("PUSH: Found existing value");
				wavestacks.get(0).pushstackVal(Float.toString(stackVal));
			} else {
				log.info("PUSH: Adding new value");
				WaveStack wavestack = new WaveStack();
				wavestack.setwaveID(wavelet.getWaveId());
				wavestack.pushstackVal(Float.toString(stackVal));
				pm.makePersistent(wavestack);
			}
		} finally {
			if (!pm.isClosed()) {
				pm.close();
			}
		}
		answerText = ("I added this value to the stack: " + Float.toString(stackVal));
		blipChildWriter(e,answerText);
	}

	private void commandHelp(Wavelet wavelet) {
		Blip helpblip = wavelet.appendBlip();
		TextView helptextView = helpblip.getDocument();
		helptextView.append("Now that you ask, there are a few things you can do.\n");
		helptextView.append("General Commands:\n");
		helptextView.append("  /name Action: Prints my name.\n");
		helptextView.append("  /creator Action: Prints my creator.\n");
		helptextView.append("  /cleanhouse Action: Adds sweepy-bot to the wave.\n");
		helptextView.append("  /help Action: You are here.\n");
		helptextView.append("Title Commands:\n");
		helptextView.append("  /savetitle Action: Save the wave title.\n");
		helptextView.append("  /printtitle Action: Print the wave title.\n");
		helptextView.append("  /printtitle /allwaves Action: Print the titles from all the waves which have been saved.\n");
		helptextView.append("Stack Commands:\n");
		helptextView.append("  /push <val> Action: Push a value to the stack.\n");
		helptextView.append("  /pop Action: Pops the top value off the stack.\n");
		helptextView.append("  /addstack Action: Adds the values on the stack for the current wave.\n");
		helptextView.append("  /addstack /allwaves Action: Adds the values on any active stack created in any wave.\n");
		helptextView.append("  /printstack Action: Prints the values on the stack for the current wave.\n");
	}
	
	private void commandAddWave(Wavelet wavelet) {
		blipWriter(wavelet,"Thanks for inviting me to wave. Type /help for help.");
	}

	private void commandCleanHouse(Event e, Wavelet wavelet) {
		String answerText;
		answerText = ("I don't do floors or windows.  You might want to try a Roomba instead? ");
		answerText = answerText.concat("I have also added sweepy-bot to help clean up a bit!  He gets rid of empty blips.");
		wavelet.addParticipant("sweepy-wave@appspot.com");
		blipChildWriter(e,answerText);
	}

	private void commandCreator(Event e) {
		String answerText;
		answerText = ("I was created by shawnsmithj@googlewave.com. Thanks for asking!");
		blipChildWriter(e,answerText);
	}

	private void commandName(Event e) {
		String answerText;
		answerText = ("My Name is ChezsmithyBot (chezsmithy-bot@appspot.com)! Thanks for asking.");
		blipChildWriter(e,answerText);
	}

	private void blipWriter(Wavelet wavelet,String blipText) {
		if (!(blipText.equalsIgnoreCase(""))) {
			Blip newblip = wavelet.appendBlip();
			TextView newtextView = newblip.getDocument();
			newtextView.append(blipText);
		}
	}
	
	private void blipChildWriter(Event e,String blipText) {
		if (!(blipText.equalsIgnoreCase(""))) {
			Blip newblip = e.getBlip().createChild();
			TextView newtextView = newblip.getDocument();
			newtextView.append(blipText);
		}
	}
	
	private enum Command
	{
		ADDWAVE,SAVEBLIP,PRINTBLIPS,POP,PUSH,NAME,CREATOR,CLEANHOUSE,HELP,ADDSTACK,PRINTSTACK,DELETE,SAVETITLE,PRINTTITLE,DEFRIEND,NOVALUE;
		
		public static Command toCommand(String str) {
			try {
				return valueOf(str.toUpperCase());
			}
			catch (Exception ex) {
				return NOVALUE;
			}
		}
	}
}
